/**
 * 
 */
package eTaxi;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

/**
 * @author Marco Luisi
 * 
 */

public class UserHandler extends Thread {

    static InetAddress taxiAddr;

    static String taxiIp;

    static int mcast_port_t = 18888;

    static int mcast_port_ut = 19999;

    static int taxiport = 15555;

    static String taxiname;

    protected TaxiProfile tp; // private ok?

    static InetAddress mcast_group;

    static MulticastSocket taxis;

    static MulticastSocket etaxi;

    static byte[] buffer;

    static DatagramPacket call;

    static String[] rcvd;

    static InetAddress userAddr;

    static int user_answer_port;

    static ServerSocket taxi_ssocket;

    static Socket taxi_socket;

    static BufferedReader reader;

    static String str;

    String cct;

    String username;

    int req_ride;

    int user_port;

    Integer res_number;

    String[] ride_details;

    boolean sync;

    BankHandler bankhandler;

    protected Lock lock; //

    protected Lock lock2;

    // mettere i timeout sui messaggi dal taxi
    Taxi taxi;

    public UserHandler(TaxiProfile t, Lock l, Lock l2) {
        super();

        this.tp = t;

        ride_details = new String[6];
        ride_details[0] = " ";
        this.lock = l;
        this.lock2 = l2;

    }

    public void run() {

        // RICORDATI IL DISCORSO DEI VALORI DAL PROFILO.. lo si crea nel main e
        // si passa i thread che tanto non lo modificano
        // .. ANCHE X L'UTENTE!

        buffer = new byte[10000];
        call = new DatagramPacket(buffer, buffer.length);
        taxiname = tp.name;
        taxiIp = tp.address;
        taxiport = tp.port;

        join_mcast();

        boolean error = false;
        // lock = new Boolean(true); lo inizializzo dal costrtuttore altrimenti
        // la-ktro prende nulklk point
        // ciclo principale del thread
        while (true) {

            /*
             * Qui il UserHandler aspetta che il thread che lavora con la banca
             * abbia finito con la precedente corsa e dia il via libera per
             * gestire un'altra chiamata.
             * 
             */

            tp.go();

            /*
             * Userhandler ha avuto il via libera e attende la richiesta sul
             * socket multicast; in caso di errore setta la variabile error a
             * false.
             */

            try {

                taxi_ssocket = new ServerSocket(taxiport);
                System.out.println("Il Taxi " + taxiname
                        + " ? in attesa di ricevere una chiamata.");

                etaxi.receive(call);

                String call_string = new String(call.getData());
                String[] aux = call_string.split("\n");
                
                etaxi.disconnect();

                etaxi.receive(call); // 

                

                System.out
                        .println("Il Taxi "
                                + taxiname
                                + " ha ricevuto una chiamata da un utente del servizio E-Taxi.");
                System.out.println("call: " + call_string);

                rcvd = aux[0].split(" ");

                System.out.println("rcvd[0]" + rcvd[0]);
                System.out.println("rcvd[1]" + rcvd[1]);
                System.out.println("rcvd[2]" + rcvd[2]);

                if (!rcvd[0].equalsIgnoreCase("REQ")) {
                    System.out
                            .println("Errore nelle comunicazioni con il cliente, ho ricevuto un messaggio !");
                    error = true;
                }

               

                /*
                 * UserHandler risponde alla chiamata inviando un datagram con i
                 * propri dettagli all'utente che ha richiesto un taxi.
                 */

                userAddr = InetAddress.getByName(rcvd[2]);
                int user_answer_port = Integer.parseInt(rcvd[3]);

                DatagramSocket answer_skt = new DatagramSocket();
                answer_skt.connect(userAddr, user_answer_port);

                String answer = "REQ OK " + taxiname + " " + taxiIp + " "
                        + taxiport + " \n";

                byte[] answer_bytes = answer.getBytes();
                DatagramPacket answer_pkt = new DatagramPacket(answer_bytes,
                        answer_bytes.length);

                System.out.println("Il Taxi " + taxiname + " ha risposto a "
                        + userAddr + " con il messaggio:");
                System.out.println(answer);
                answer_skt.send(answer_pkt);
                answer_skt.close();

            }

            /*
             * In caso di problemi nella comunicazione della disponibilit?
             * all'utente si setta error a true
             */

            catch (IOException e) {
                System.out
                        .println("Errore nelle comunicazioni con il cliente : "
                                + e);
                error = true;

            } // fine catch MULTICAST

            /*
             * A questo punto il Taxi attende che il cliente invii i propri
             * dati; in caso di timeout setta error a true;
             * 
             */

            try {
                /*
                 * MAGARI CONTROLLARE SE LO STESSO A CUI SI E' INVIATO IL
                 * MESSAGGIO PRIMA SIA LO STESSO CHE CI RISPONDE ORA!!!!!
                 */
                System.out.println("Il Taxi " + taxiname
                        + " attende il messaggio di richiesta da " + userAddr); 

                taxi_ssocket.setSoTimeout(4000);
                taxi_socket = taxi_ssocket.accept();

                reader = new BufferedReader(new InputStreamReader(taxi_socket
                        .getInputStream()));
                str = reader.readLine();
                System.out
                        .println("Il Taxi "
                                + taxiname
                                + " ha ricevuto il primo messaggio con i dettagli della richiesta dall'utente."); 
                error = false;
                ride_details = str.split(" ");
                if (!ride_details[0].equalsIgnoreCase("RES")) {
                    System.out
                            .println("Errore nelle comunicazioni con il cliente, ho ricevuto un messaggio errato!");
                    error = true;
                }

            }

            catch (SocketTimeoutException ste) {

                error = true;
                System.out
                        .println("Errore: l'utente non ha risposto in tempo; errore di timeout");
                try {

                    taxi_ssocket.close();

                }

                catch (IOException ioe) {
                    error = true;
                }

            }

            catch (IOException ioe) {

                error = true;

            }

            if (!error)

            {

                /*
                 * Se non ci sono stati problemi nella comunicazione con il
                 * cliente, si procede con la gestione della corsa.
                 * 
                 */

                // si sblocca bankhandler
                lock.setLock(false);

                String userAddr = ride_details[2];

                int userPort = Integer.parseInt(ride_details[3]);
                req_ride = Integer.parseInt(ride_details[4]);

                
                /*
                 * Si imposta il costo della corsa in base al tipo di corsa
                 * chiesto dal cliente;
                 */

                int fee = 0;

                switch (req_ride) {

                case 1:
                    fee = 5;
                    break;
                case 2:
                    fee = 15;
                    break;
                case 3:
                    fee = 30;
                    break;

                }

                System.out.println("La corsa al cliente costa " + fee);

                /*
                 * Questo thread deve ora sincronizzarsi con BankHandler, lo fa
                 * con il monitor lock2
                 * 
                 */

                while (lock2.go()) { // forse non serve un while

                }
                /*
                 * Subito dopo essere stato sbloccato, fa in modo di bloccare
                 * BankHandler che altrimenti comunicherebbe alla banca i
                 * parametri della transazione in corso.
                 */

                lock.setLock(true);

                /*
                 * Con un blocco synchronized si scrive uno spazio nella prima
                 * posizione di ride_details di modo che BankHandler si blocchi
                 * fino a che questo thread non riceva un'altra chiamata. Non ??
                 * possibile che BankHandler sia all'inizio del proprio ciclo
                 * principale in quanto deve prima aspettare la notifica da
                 * parte del cliente, che avverrà dopo questo blocco
                 * synchronized.
                 */

                int res_int = bankhandler.res_number.intValue();

                /*
                 * UserHandler accede alla variabile res_number di BankHandler
                 * per comunicare al cliente l'esito della transazione con la
                 * banca
                 */

                try {
                    Socket service_skt = new Socket(userAddr, userPort);
                    DataOutputStream service_stream = new DataOutputStream(
                            service_skt.getOutputStream());

                    if (res_int == 0) {
                        System.out
                                .println("La banca ha negato la transazione, lo comunico al cliente.");
                        String negative = "RES FAIL \n";

                        service_stream.flush();
                        service_stream.writeBytes(negative);

                    }

                    else {

                        System.out
                                .println("La banca ha approvato la transazione, lo comunico al cliente.");
                        String positive = "RES OK " + res_int + " \n";

                        System.out
                                .println("Messaggio al cliente : " + positive);

                        service_stream.writeBytes(positive);

                        String payment_details = "PAYMENT " + fee + " "
                                + res_int + " \n";

                        service_stream.flush();
                        service_stream.writeBytes(payment_details);

                    }

                    service_skt.close();
                    taxi_socket.close();
                    taxi_ssocket.close();

                } // fine try

                catch (IOException e) {
                    System.out.println(e);
                }

            }// fine if error

            else { // c'è stato un errore e resetto il lock a true
                lock.setLock(true);

                try {

                    taxi_socket.close();
                    taxi_ssocket.close();
                }

                catch (IOException e) {
                    System.out.println(e);
                }

            }

        }// fine while principale

    } // fine run

    private static boolean join_mcast() {

        try {
            mcast_group = InetAddress.getByName("224.0.0.1");//

            taxis = new MulticastSocket(mcast_port_t);
            taxis.joinGroup(mcast_group);

            etaxi = new MulticastSocket(mcast_port_ut);
            etaxi.joinGroup(mcast_group);
            System.out.println("Il taxi " + taxiname
                    + " ?? registrato nell'applicazione E-Taxi...");

            return true;
        }

        catch (IOException ioe) {

            System.out
                    .println("Problemi nel collegamento al gruppo multicast!"); // fuck
            System.out.println(ioe);

            return false;
        }

    }

    protected void init(BankHandler bh) {
        this.bankhandler = bh;

    }
}
// fine classe

